package bold.redisson;

import lombok.extern.log4j.Log4j2;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.util.concurrent.TimeUnit;

/**
 * redis 缓存方法代理
 * 锁的颗粒太大
 * 需并发测试
 */
@Log4j2
@Aspect
@Component
@Order(2)
public class LockAspect {

    @Autowired
    private RedissonClient redissonClient;

    @Around("@annotation(lock)")
    public Object around(ProceedingJoinPoint point, Lock lock) throws Throwable {
        MethodSignature ms = (MethodSignature) point.getSignature();

        Object[] args = point.getArgs();
        if (args == null || args.length == 0) {
            throw new RuntimeException("方法没有参数");
        }

        //1.获取参数上的key
        String key;
        Object arg = args[0];
        if (arg instanceof String || arg instanceof Number) {
            key = String.valueOf(args[0]);
        } else {
            if (StringUtils.isEmpty(lock.field())) {
                throw new RuntimeException("Lock中field不能为空");
            }
            Field fd = ReflectionUtils.findField(arg.getClass(), lock.field());
            fd.setAccessible(true);
            Object obj = fd.get(arg);
            key = String.valueOf(obj);
        }
        String redisKey = lock.prefix() + "_key_" + key;

        //2.获取锁
        RLock rLock = redissonClient.getLock(redisKey);

        Object result = null;
        try {
            rLock.lock();
            result = point.proceed();
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.err.println("获取锁异常");
            throw new RuntimeException("获取锁异常");
        } catch (Throwable e) {
            e.printStackTrace();
            throw e;//程序异常
        }
        return result;
    }

}
